This document has styles in it. You may view them with a text editor such as Tex-Edit by Tom Bender or Style (1.2.1 or later) by Marco Piovanelli.
If you do not care for the styles and cannot live without EasyView (a neat application by M. Akif Eyler) try opening this document with EasyView (with the same settings as TidBITS).
This documentation is intended as a little guide to the simulator; if you are in trouble, remember that Balloon Help™ is always available. You are strongly encouraged to use Balloon Help™ so that you can “learn by doing”, and discover some intuitive but not immediately visible functionalities. However, I have to admit that to effectively use this application (and fully understand this document), you should know something about the (wonderful?) world of microprogramming.
Why freeware?
-------------
This application is aimed at students or teachers, who are usually not millionaires. Anyway, if you have a couple (well, hundreds are even better) of bucks to spare and feel like sending them to me, you are welcome (I will use them to buy a new compiler, so that the Simulator will get smaller & faster).
Why beta?
---------
Most Mac users will probably find this application totally useless, but students or teachers may like and use it. Somebody may even have ideas about how to modify this application to fit their particular needs (or fix bugs, of course). Write to me and you will be heard.
Overview
--------
µArchitecture Simulator models a microprogrammed processor similar to the one described in the book “Structured Computer Organization” by Andrew S. Tanenbaum. Its hardware components and its instruction set are fixed (not too much, as we will see) but its microprogram is fully editable in a user friendly manner. The processor has access to a 128K Random Access Memory (it borrows from your Mac); you can easily view, modify, load or save this portion of memory. You can run programs and debug them with a step by step execution. Namely, you can advance by a conventional instruction, by a microinstruction, and even by a clock subcycle observing the internal parts of the processor.
You can work on only one document (microprogram) at a time.
The instruction set
-------------------
I have largely expanded the instruction set described in the Tanenbaum’s book; the instruction list is available in a separate document you should have got with this one. I do not like this instruction set (as most people accustomed with MC68000’s will concur), but I had to use this one (why? the answer is in the section titled Final thoughts).
By the way, I have renamed the instruction whose name was “LOCO” in the book to “MOVQ” because incidentally LOCO #0 (which puts a zero in the Accumulator) had the same opcode ($7000) as MC680x0’s MOVEQ #0,D0 (which puts a zero in the register D0).
The instruction set is fixed; in particular, it is stored entirely in a resource of type “OPCO”. Technically inclined people can easily modify the instruction set using ResEdit (I have included a template), and even add or delete instructions. My code should assemble and disassemble instructions correctly, provided that you follow the right format.
Throughout this documentation, Balloon Help™, and so on, I always refer to the instructions cited here as “conventional instructions”. I don’t use the term “macroinstructions” (used by Tanenbaum) to avoid confusion with “macros” such as #define in the C language.
The microprogram
----------------
I have tried to simplify as much as possible the uneasy task of writing and debugging a microprogram by taking advantage of the Macintosh user interface friendliness. All of the microprogram is represented in the Microprogram window (I am sure you already guessed this one). From left to right, you can see: the conventional instructions list; the microinstructions list; a set of various controls.
The microinstruction list will appear empty if you start up the simulator without opening a document. If you open one of the sample documents supplied with this application, the microinstruction list fills up with comment strings. Their purpose is to allow the microprogrammer to read a brief description of the actions performed by each microinstruction; I have written these strings using a C-style syntax, but any style is permitted, since their contents is ignored during execution.
You control the behavior of a microinstruction with the buttons and pop-up menus placed at the right side. The Animation window dynamically updates to give you some visual cues about the currently selected microinstruction.
The hardware decoding unit
--------------------------
It is the major change to the Tanenbaum’s example. Instead of decoding fetched instructions with the microprogram (a slow business), the processor calls a hardware mechanism which does the job. Namely, the hardware decoder sets the Micro Program Counter to the starting line of the fetched instruction. Illegal instructions are handled as in a real processor: a jump vector is read into the Program Counter from location 3 (in my example); the illegal instruction handler can be inserted into the resource of type ‘HAND’ (currently a HALT instruction).
I noticed that most conventional instructions were extracting (from the instruction opcode) the lower 12 bits (with sign extension), the lower 11 bits (again with sign extension), or the lower 8 bits (no sign extension this time). So I set up the X12, X11, Lo8 registers for this purpose; the hardware decoder can set these registers in parallel.
These changes made the Temporary Instruction Register and mask registers useless; I ended with one more register and a somewhat faster machine.
The registers
-------------
This processor has sixteen 16-bit registers. Three of them are read-only: they contain the constant values -1, 0, 1. Other three (X12, X11, Lo8) receive data from the hardware decoding unit. Then you can find (as in most microprocessors) a Program Counter and a Stack Pointer; a Base Pointer is provided to simplify access to local variables. The Accumulator is the register used in most of the conventional instructions of this processor. The Instruction Register is used to hold conventional instructions as they are fetched from the main memory. The other registers (A, B, C, D, E) are not visible at the conventional machine level (with my instruction set, of course), and can be used by the microprogram as scratch registers. In the example microprogram supplied, only A is used.
The assembler
-------------
It is probably the worst part in this application. It cannot process ORG directives, EQUates or literals, and error checking and reporting is very superficial. However, a correct program is guaranteed to assemble without hassles and real fast.
System 7 & around
-----------------
If you launch the simulator under systems older than 7.0, you will get a sorrowful message. That is because System 7 is so convenient from a programmer point of view, that I decided to cut off System 6 support. Let’s examine these new opportunities:
Apple Events let you drag-and-drop microprogram, memory, registers and text documents onto µArchitecture Simulator’s icon. My application will respectively open the microprogram, read the memory image, restore the registers and assemble the program contained in the text document without a hitch. I also internally use Apple Events to signal important facts happening in the simulation, such as stack underflows/overflows, etc. Note that the Apple Event Manager automatically flashes an icon in the menu bar if these events happen while the application is running in the background.
The Dialog Manager extensions are used to display an I-beam cursor when you move the cursor over a text editing area, to provide the standard black border around the default button, and to assign the standard key equivalents to the main two buttons (Return/Enter, Command-. and Esc).
The Folder Manager is used to save the Preferences document in the right folder, and to perform a safe-save of documents: the new version of a document is saved in a hidden folder (named “Temporary Items”); thus if a system crash occurs while saving, the original version remains safely unchanged, and the new version should be found in the Trash after restarting.
The Help Manager is used for Balloon Help™, which provides a unique non-modal help system; I think it is very useful, provided that the balloons are written to really explain items on screen.
The standard Pop-up menus are used everywhere: if you find out that they redraw slowly, blame Apple (not me).
The new dialogs for opening and saving documents provide a mechanism to support System 7 stationery pads: when you open a stationery pad, the application opens the document in a new untitled window.
I also use a cool feature present in System 6.0.5 or later, which allows text selections to be nicely shown when the text is not active.
If the simulator goes out of memory, I save the main memory, the registers, and the microprogram you are presumably working at in the Temporary Items folder. The Finder will gently place them (after you restart the Macintosh) in the Trash into a folder named “Rescued Items from <name of your startup disk>” (the denomination depends on the System localization). So you should not lose any data.
Finally, Temporary Memory is used (when available) to execute tasks (such as reading from documents or building tables for the assembling process) requiring amounts of memory for a limited time. Under System 7 this feature is very handy, because you can play with Temporary Memory using the ordinary Memory Manager calls (under System 6 you have to use special calls).
An interesting memo for the Mac hacker
--------------------------------------
(could be a proper subject for the KON&BAL column in d e v e l o p)
Are you interested in low-level stuff? Have you read Inside Mac at least I through IV? Follow me, then.
Remember the List Manager? Remember the lClikLoop field? No? Get Inside Mac IV, page 266. Are you ready now?
So let’s assume you are handling a list (a normal, one-column one) and are writing a function that will be called repeatedly by the LClick function. Suppose you don’t want to mess up with assembler. Plain C:
pascal Boolean myClikLoop(void)
{
register long something;
/* your code works with “something” */
return true; /* we never wish to abort LClick */
}
You play with your list, and you notice that when you click on it, and drag to the first element, it looks like you have released the mouse button. But you have really not released the mouse button. What’s going on?
When you click on a list, you call LClick as Inside Mac IV told you. It seems that LClick can abort only in two eventualities: either you are releasing the mouse button or your lClikLoop routine returns false. But you are definitely not releasing the mouse button, nor returning false anytime: there is a clear return true statement! Everything seems right, but the thing refuses to work. You read on Inside Mac (on the same page) in an Assembly-language note: your routine should set register D0 to 1; returning 0 in register D0 aborts LClick. You add an asm { MOVEQ #1,D0 } statement before returning, but it still doesn’t work. Big headache, isn’t it?
To answer the riddle, you have to taste some details about 68000 assembly language. For example, did you know that the MOVE instruction sets the condition codes? Namely, in the following assembler sequence:
MOVE.W (SP)+,Dx
TST.W Dx
BEQ @somewhere
the TST instruction is useless and can be eliminated. (As far as I know, the Intel 8086 nearly equivalent instruction - MOV - does not set the condition codes).
Let’s have a look at the assembler listing of our routine (a sort of):
LINK A6,#xx
MOVE.L D7,-(SP) /* saves the register we will use */
… /* the “something” variable is into D7 */
MOVEQ #1,D0 /* you added this, didn’t you? */
MOVE.B #1,8(A6) /* the compiler returns true */
MOVE.L (SP)+,D7 /* restores the register */
UNLK A6
RTS
I will now show you what happens when you execute this routine. Assume you have dragged onto the first line of the list. The Mac (the MC680x0 microprocessor) executes your routine. It gets till the RTS. Step one instruction. You now are facing a BEQ.S @somewhere instruction. The operating system is not checking explicitly your return value. It is optimized for the 680x0 family: it is assuming that you have moved the true result somewhere just before returning. This is a good assumption in 99% of the cases. In our circumstance the last MOVE is the one which restores the D7 register. So the condition codes are set according to the D7 contents! It happens that D7 (when entering the lClikLoop routine) contains the coordinates of the cell underlying the mouse. The first line is (0,0), so D7 contains a big 0. Hence the operating system thinks you have returned false (false is represented numerically as a zero). This kind of thing happens only nowadays with optimizing compilers. Back in the old days of not-too-optimizing Pascal compilers, they used to save and restore registers always with the MOVEM (MOVE Multiple registers) instruction. This instruction does not set the condition codes. A clever compiler sees that it has only to save one register, so it generates a faster and shorter MOVE.
What’s the sequitur? If you were writing a Pascal or C function like that, and you didn’t know assembler, you’d better gibbet yourself on a tree and “swing in the breeze”.
The no-resume-event syndrome
----------------------------
No, you don’t need a doctor. Unless you are using the nifty Apple Event Manager function AEInteractWithUser and strange things happen. If you use this function to bring your application to the front, remember that the System does not send you a resume event.
QuicKeys user, read this!
-------------------------
I found out that QuicKeys 2™ (version 2.1.1) by CE Software calls NewHandle(0x10000) when invoked: this will try to allocate a 64K block in my own heap. If NewHandle succeeds, they infer that enough memory is available for QuicKeys, dispose the dummy handle just created, open QuicKeys, and so on; if the call fails they open QuicKeys in temporary memory. There is a slight problem when using this technique: if NewHandle fails, the grow zone function of the current application is called, if present. You can install a grow zone function to gracefully handle low memory situations: for instance, trying to save your documents and quit without bombing. CE Software programmers should call MaxBlock and PurgeSpace to check if they have enough memory in the application zone; this would leave my grow zone function calm and quiet. Note that their behavior can put in trouble every application using a grow zone function. So if you notice that my application quits if you call QuicKeys, blame CE Software (not me). To work around their bug, simply give a little more memory to my application, so that their NewHandle call does not fail (and my grow zone function does not get called).
This bug may be absent from subsequent releases of QuicKeys 2.
Final thoughts
--------------
I was nearly forgetting that there is a hidden feature you can easily activate peeking around with ResEdit…
In case you are wondering, I have written this application because of a university exam.
Thanks to…
----------
My parents, who bought my first Macintosh (a Plus!) back in 1986 and pay for my studies, etc., etc.;
Alessandro Levi Montalcini, for having borrowed me Inside Macintosh 6 when bookstores in Torino hadn’t even heard of it; and for many, many useful suggestions (some of them still remain suggestions, unfortunately), for reporting bugs, and for having written a bunch of very useful utilities;
Alberto Ricci, for (again) many, many useful suggestions, untiring testing and bug reporting;
Peppino Ventura, who tested as always on his Mac Classic;
… and thanks to whoever has contributed to make the Mac what it is.